/**
  ******************************************************************************
  * @file    main.c
  * @author  AE Team
  * @version 1.0.3
  * @date    2024-05-28
  * @brief   本样例演示 RTC 采用 LSE 进行补偿，管脚(PB06)输出1Hz计数信号
  *          GTIM1-ATIM级联实现32位输入捕捉以测量RTC1Hz信号的周期。
  *          需将RTC1Hz(PB06)连接到GTIM1.CH2(PB05)和ATIM.CH1(PB04)
  *
  *          通过串口打印每一秒的LSE时钟个数、本秒补偿的LSE数量，32秒累加补偿的LSE数量
  *          串口参数为115200-8-n-1, TXD=PB02
  *
  * @note    硬件平台：SYM32L010_StartKit_V1.0
  *          接线说明：printf打印需要连接USB线缆，请短接J13、断开J12
  *                    RTC使用外部低速时钟LSE，   请短接J9、J10, 断开J8、J11
  *                    系统时钟来自HSE，          请短接J5、J6
  *
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2024 SIYIMicro.
  * All rights reserved.</center></h2>
  *
  *
  ******************************************************************************
  */


/******************************************************************************/
/* Include files                                                              */
/******************************************************************************/
#include "main.h"
#include "sym32l010_hal_sysctrl.h"
#include "sym32l010_hal_systick.h"
#include "sym32l010_hal_gpio.h"
#include "sym32l010_hal_rtc.h"
#include "sym32l010_hal_gtim.h"
#include "sym32l010_hal_atim.h"
#include "sym32l010_startkit_board.h"


/******************************************************************************/
/* Local pre-processor symbols/macros ('#define')                             */
/******************************************************************************/

/******************************************************************************/
/* Global variable definitions (declared in header file with 'extern')        */
/******************************************************************************/
ATIM_HandleTypeDef hatim;
GTIM_HandleTypeDef hgtim1;

/******************************************************************************/
/* Local type definitions ('typedef')                                         */
/******************************************************************************/

/******************************************************************************/
/* Local function prototypes ('static')                                       */
/******************************************************************************/
static void SysInit(void);
static void RTC_Init(void);
static void GTIM_Init(void);
static void ATIM_Init(void);

static uint8_t  IsCaptured(void);
static uint32_t GetCaptureValue(void);

/******************************************************************************/
/* Local variable definitions ('static')                                      */
/******************************************************************************/

/******************************************************************************/
/* Local pre-processor symbols/macros ('#define')                             */
/******************************************************************************/

/******************************************************************************/
/* Function implementation - global ('extern') and local ('static')           */
/******************************************************************************/


/*******************************************************************************
 ** \brief  Main function of project
 **
 ** \return uint32_t return value, if needed
 **
 **
 ******************************************************************************/
int32_t main(void)
{
    // 系统时钟初始化
    SysInit();

    // 板载 UART 初始化
    Bsp_Uart_Init(8000000, 115200);
    printf("HelloWorld!\r\n");

    // GTIM 初始化
    GTIM_Init();

    // ATIM 初始化
    ATIM_Init();

    // RTC 初始化
    RTC_Init();

    // 配置并使能补偿功能，每32秒增加或减少15个LSE时钟
    HAL_RTC_Compen_Config(RTC_COMPEN_PERIOD_32S, RTC_COMPEN_SIGN_INC, 15); // 正向补偿，增加LSE时钟
    //HAL_RTC_Compen_Config(RTC_COMPEN_PERIOD_32S, RTC_COMPEN_SIGN_DEC, 15); // 反向补偿，减少LSE时钟
    HAL_RTC_Compen_Disable();

    // RTC1Hz管脚(PB06)输出实际的秒计数波形
    // 将PB06连接到GTIM1.CH2(PB05)，GTIM1捕捉RTC1Hz信号的上升沿
    // 将PB06连接到ATIM.CH1(PB04)， ATIM 捕捉RTC1Hz信号的上升沿
    // GTIM1/ATIM级联，实现32位的输入捕捉以测量RTC1Hz信号的周期

    //------------------------------------------------------------
    // Step1, 禁止补偿，测量LSE的周期
    uint64_t CapOld, CapNew;
    double LsePeriod;
    for (uint8_t i=0; i<4; i++)
    {
        CapOld = CapNew & 0xFFFFFFFF;
        while (!IsCaptured());
        CapNew = GetCaptureValue();
    }
    CapNew = (CapNew < CapOld) ? (CapNew + 0x100000000) : CapNew;
    LsePeriod  = CapNew - CapOld;
    LsePeriod /= 32768;

    //------------------------------------------------------------
    // Step2, 使能补偿，测量每一秒有多少个LSE时钟，以计算实际补偿值
    // 通过串口打印每一秒的LSE时钟个数、本秒补偿的LSE数量，32秒累加补偿的LSE数量
    HAL_RTC_Compen_Enable();
    uint8_t Idx = 28;
    int     CompenCnt;
    while (1)
    {
        CapOld = CapNew & 0xFFFFFFFF;
        while (!IsCaptured());
        CapNew = GetCaptureValue();
        CapNew = (CapNew < CapOld) ? (CapNew + 0x100000000) : CapNew;
        int LseCount = (int)((CapNew - CapOld + LsePeriod/2) / LsePeriod);
        int Delt     = LseCount - 32768;
        CompenCnt    = CompenCnt + Delt;

        printf("Idx= %2d    LseCnt = %d    Delt = %d    CompenCnt = %d\n", Idx, LseCount, Delt, CompenCnt);
        Idx++;
        if (Idx >= 33)
        {
            Idx = 1;
            CompenCnt = 0;
            printf("\n\n========================================================\n");
            printf("User set compen = 15, Mcu will measure actual compen in 32 seconds...\n");

        }
    }
}


/*******************************************************************************
 * @brief  RTC初始化
 * @retval None
 */
static void RTC_Init(void)
{
    GPIO_InitTypeDef            GPIO_InitStruct;
    RTC_Calendar_InitTypeDef    RTC_InitStruct;

    MemClr(&GPIO_InitStruct, sizeof(GPIO_InitStruct));
    MemClr(&RTC_InitStruct,  sizeof(RTC_InitStruct));

    // 使能外设时钟
    HAL_SYSCTRL_RTC_CLK_ENABLE();
    HAL_SYSCTRL_GPIOB_CLK_ENABLE();

    AFx_RTCOUT_PB06();
    GPIO_InitStruct.Pins = GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    HAL_GPIO_Init(SYM_GPIOB, &GPIO_InitStruct);

    // 使能并锁定LSE
    while (HAL_SysCtrl_LSE_Enable_Crystal(SYSCTRL_LSE_DRIVER_5) != HAL_OK) {;}
    HAL_SysCtrl_LSE_Lock();

    // 配置并运行 RTC 日历
    RTC_InitStruct.ClockSource = RTC_CLKSRC_LSE;                      // 采用LSE作为RTC的时钟
    RTC_InitStruct.HourMode    = RTC_HOUR_24H;                        // 24小时制
    RTC_InitStruct.Year        = 0x24;                                // BCD码  2024-04-07
    RTC_InitStruct.Month       = 0x04;
    RTC_InitStruct.Day         = 0x07;
    RTC_InitStruct.Week        = 0x00;
    RTC_InitStruct.Hour        = 0x11;                                // BCD码  11:00:15
    RTC_InitStruct.Minute      = 0x00;
    RTC_InitStruct.Second      = 0x15;
    RTC_InitStruct.Interval    = RTC_INTERVAL_NONE;
    HAL_RTC_Calendar_Init(&RTC_InitStruct);
    HAL_RTC_Calendar_Start();

    // 配置并输出RTC1Hz
    HAL_RTC_1Hz_Config(RTC_1HZ_RTCCLK);
}


/*******************************************************************************
 * @brief  系统初始化
 * @retval None
 */
static void SysInit(void)
{
    HAL_StatusTypeDef res;

    // 使能HSE8M
    res = HAL_SysCtrl_HSE_Enable_Crystal(8000000, SYSCTRL_HSE_DRIVER_3);
    if (res != HAL_OK)
    {
        // HSE起振失败，请检查HSE的连接情况
        while (1) { ; }
    }

    // 切换系统时钟为HSE
    HAL_SysCtrl_SysClk_Switch(SYSCTRL_SYSCLKSRC_HSE);

    // 使能外设时钟
    HAL_SYSCTRL_GPIOA_CLK_ENABLE();
    HAL_SYSCTRL_GTIM1_CLK_ENABLE();
    HAL_SYSCTRL_ATIM_CLK_ENABLE();
}


/*******************************************************************************
 * @brief  检查是否发生了捕捉事件
 * @retval 0: 未发生
 *         1: 发生了捕捉事件
 */
uint8_t IsCaptured(void)
{
    if (HAL_GTIM_GET_FLAG(&hgtim1, GTIM_IT_FLAG_CC2) &&
        HAL_ATIM_GET_FLAG(&hatim, ATIM_IT_FLAG_CC1))
    {
        HAL_GTIM_CLR_FLAG(&hgtim1, GTIM_IT_FLAG_CC2);
        HAL_ATIM_CLR_FLAG(&hatim,  ATIM_IT_FLAG_CC1);
        return(1);
    }

    return(0);
}


/*******************************************************************************
 * @brief  获取ATIM输入捕捉值
 * @retval 返回32比特的输入捕捉值
 */
uint32_t GetCaptureValue(void)
{
    uint32_t tmp32;

    tmp32   = SYM_ATIM->CCR1;
    tmp32 <<= 16;
    tmp32  |= SYM_GTIM1->CCR2;

    return(tmp32);
}


/*******************************************************************************
 * @brief  GTIM 初始化
 * @retval None
 */
static void GTIM_Init(void)
{
    GPIO_InitTypeDef         GPIO_InitStruct;
    GTIM_MasterConfigTypeDef GTIM_MasterCfgStruct;

    MemClr(&GPIO_InitStruct,      sizeof(GPIO_InitStruct));
    MemClr(&GTIM_MasterCfgStruct, sizeof(GTIM_MasterCfgStruct));

    // 初始化GTIM1输入捕捉通道2 @ PB05
    AFx_GTIM1CH2_PB05();
    GPIO_InitStruct.Pins = GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULL_UP;
    HAL_GPIO_Init(SYM_GPIOB, &GPIO_InitStruct);

    // 初始化GTIM1为计数器模式
    MemClr(&hatim, sizeof(hatim));
    hgtim1.Instance           = SYM_GTIM1;
    hgtim1.Init.Period        = 65536 - 1;
    hgtim1.Init.Prescaler     = 1 - 1;
    hgtim1.Init.CounterMode   = GTIM_COUNTERMODE_UP;
    HAL_GTIM_IC_Init(&hgtim1);

    hgtim1.IcInit.ICFilter    = 0;
    hgtim1.IcInit.ICPolarity  = GTIM_ICPOLARITY_RISING;
    hgtim1.IcInit.ICPrescaler = GTIM_ICPSC_DIV1;
    hgtim1.IcInit.ICSelection = GTIM_ICSELECTION_DIRECTTI;
    HAL_GTIM_IC_ConfigChannel(&hgtim1, &hgtim1.IcInit, GTIM_CHANNEL_2);

    GTIM_MasterCfgStruct.MasterOutputTrigger = GTIM_TRGO_UPDATE;      // GTIM1.TRGO = GTIM1_OV
    GTIM_MasterCfgStruct.MasterSlaveMode     = GTIM_MASTERSLAVEMODE_ENABLE;
    HAL_GTIM_MasterConfigSynchronization(&hgtim1, &GTIM_MasterCfgStruct);

    HAL_GTIM_IC_Start(&hgtim1, GTIM_CHANNEL_2);
}


/*******************************************************************************
 * @brief  ATIM 初始化
 * @retval None
 */
static void ATIM_Init(void)
{
    GPIO_InitTypeDef        GPIO_InitStruct;
    ATIM_ClockConfigTypeDef ATIM_ClkCgfStruct;

    MemClr(&GPIO_InitStruct,     sizeof(GPIO_InitStruct));
    MemClr(&ATIM_ClkCgfStruct,   sizeof(ATIM_ClkCgfStruct));

    // 初始化ATIM输入捕捉通道1 @ PB04
    AFx_ATIMCH1_PB04();
    GPIO_InitStruct.Pins = GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULL_UP;
    HAL_GPIO_Init(SYM_GPIOB, &GPIO_InitStruct);

    // 初始化ATIM为计数器模式，计数时钟来自GTIM1溢出
    MemClr(&hatim, sizeof(hatim));
    hatim.Instance           = SYM_ATIM;
    hatim.Init.Period        = 65536 - 1;
    hatim.Init.Prescaler     = 1 - 1;
    hatim.Init.CounterMode   = ATIM_COUNTERMODE_UP;
    HAL_ATIM_IC_Init(&hatim);

    hatim.IcInit.ICFilter    = 0;
    hatim.IcInit.ICPolarity  = ATIM_ICPOLARITY_RISING;
    hatim.IcInit.ICPrescaler = ATIM_ICPSC_DIV1;
    hatim.IcInit.ICSelection = ATIM_ICSELECTION_DIRECTTI;
    HAL_ATIM_IC_ConfigChannel(&hatim, &hatim.IcInit, ATIM_CHANNEL_1);

    ATIM_ClkCgfStruct.ClockFilter    = 0;
    ATIM_ClkCgfStruct.ClockPolarity  = ATIM_CLOCKPOLARITY_RISING;
    ATIM_ClkCgfStruct.ClockPrescaler = ATIM_CLOCKPRESCALER_DIV1;
    ATIM_ClkCgfStruct.ClockSource    = ATIM_CLOCKSOURCE_ITR2;         // ATIM的计时时钟来源：GTIM1_TRGO
    HAL_ATIM_ConfigClockSource(&hatim, &ATIM_ClkCgfStruct);

    HAL_ATIM_IC_Start(&hatim, ATIM_CHANNEL_1);                        // 启动ATIM.CH1输入捕获
}


/*******************************************************************************
 ** EOF (not truncated)
 ******************************************************************************/
#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
    /* USER CODE BEGIN */
    /* User can add his own implementation to report the file name and line number,
       tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
    /* USER CODE END */
}
#endif /* USE_FULL_ASSERT */


/************************ (C) COPYRIGHT SIYIMicro *****END OF FILE*************/
